#include <basic/errcode.h>
#include <basic/datetime.h>
#include <basic/log.h>

int log_from_stdout(char *log_file)
{/*{{{*/
    int fd;
    char log[LONG_FILE_NM_SIZE+1];

    if(log_file)
    {
        if(strlen(log_file) > LONG_FILE_NM_SIZE)
            return(-1);
        if(strcasecmp(log_file, "stdout") == 0)
            return(0);
        strcpy(log, log_file);
    }
    else
        strcpy(log, "/dev/null");
    fd=open(log, O_RDWR | O_CREAT | O_APPEND,
            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if(fd == -1)  return(-2);
    close(1);
    dup(fd);
    close(fd);
    setbuf(stdout, NULL);

    return(0);
}/*}}}*/

enum { LOG_NONE, LOG_STDOUT, LOG_FILE };

struct _log_handle
{/*{{{*/
    unsigned int type; 
    char file_nm[LONG_FILE_NM_SIZE];
    FILE *fp;

    unsigned int flag;
    char date[DATE_SIZE+1];
    pthread_mutex_t lock;
};/*}}}*/

int log_open(char *log_file, unsigned int flag, LOG_HANDLE *phandle)
{/*{{{*/
    LOG_HANDLE handle;

    if(!phandle)
        return(RET_ERR_PARA);

    *phandle=NULL;
    ALLOC_RETERR(handle, struct _log_handle, -2);

    if(!log_file)
    {
        handle->type=LOG_NONE;
        goto OUT_SUCCESS;
    }

    if(strcasecmp(log_file, "stdout") == 0)
    {
        handle->type=LOG_STDOUT;
        handle->fp=stdout;
        goto OUT_SUCCESS;
    }

    if(log_file[0] == ZERO || strlen(log_file) > LONG_FILE_NM_SIZE)
    {
        free(handle);
        return(-3);
    }
    handle->type=LOG_FILE;
    strcpy(handle->file_nm, log_file);
    handle->fp=NULL;
    handle->flag=flag;
    strcpy(handle->date, MIN_DATE);
    if((handle->flag & (LOG_DATE_SWITCH | LOG_MULTI_THREAD)) ==
            (LOG_DATE_SWITCH | LOG_MULTI_THREAD))
        pthread_mutex_init(&handle->lock, NULL);
    if(!(handle->flag & LOG_DATE_SWITCH))
    {
        if((handle->fp=fopen(handle->file_nm, "a")) == NULL)
        {
            free(handle);
            return(-4);
        }
        setbuf(handle->fp, NULL);
    }

OUT_SUCCESS:
    *phandle=handle;
    return(0);
}/*}}}*/

void log_close(LOG_HANDLE handle)
{/*{{{*/
    if(handle)
    {
        if(handle->type == LOG_FILE && handle->fp)
            fclose(handle->fp);
        free(handle);
    }
}/*}}}*/

static int _date_switch(LOG_HANDLE handle)
{/*{{{*/
    char curr_dt[DATE_SIZE+1];

    curr_date(curr_dt);
    if(strcmp(handle->date, curr_dt) != 0)
    {
        char file_nm[LONG_FILE_NM_SIZE+1];

        if(handle->fp)  fclose(handle->fp);
        sprintf(file_nm, "%s.%s", handle->file_nm, curr_dt);
        if((handle->fp=fopen(file_nm, "a")) == NULL)
            return(-1);
        strcpy(handle->date, curr_dt);
        setbuf(handle->fp, NULL);
    }
    return(0);
}/*}}}*/

int log_write(LOG_HANDLE handle, char *format, ...)
{/*{{{*/
    va_list arglist;
    int rtn=0;

    if(!handle)
        return(RET_ERR_PARA);
    if(handle->type == LOG_NONE || !format)
        return(0);

    // 日期切换
    if(handle->flag & LOG_DATE_SWITCH)
    {
        // 多线程
        if(handle->flag & LOG_MULTI_THREAD)
            pthread_mutex_lock(&handle->lock);
        if(_date_switch(handle) == 0)
        {
            va_start(arglist, format);
            vfprintf(handle->fp, format, arglist);
        }
        else
            rtn=-1;
        // 多线程
        if(handle->flag & LOG_MULTI_THREAD)
            pthread_mutex_unlock(&handle->lock);
    }
    else
    {
        va_start(arglist, format);
        vfprintf(handle->fp, format, arglist);
    }

    return(rtn);
}/*}}}*/

int log_write_raw(LOG_HANDLE handle, char *msg)
{/*{{{*/
    int rtn=0;

    if(!handle)
        return(RET_ERR_PARA);
    if(handle->type == LOG_NONE || !msg)
        return(0);

    // 日期切换
    if(handle->flag & LOG_DATE_SWITCH)
    {
        // 多线程
        if(handle->flag & LOG_MULTI_THREAD)
            pthread_mutex_lock(&handle->lock);
        if(_date_switch(handle) == 0)
        {
            fputs(msg, handle->fp);
        }
        else
            rtn=-1;
        // 多线程
        if(handle->flag & LOG_MULTI_THREAD)
            pthread_mutex_unlock(&handle->lock);
    }
    else
    {
        fputs(msg, handle->fp);
    }

    return(rtn);
}/*}}}*/

/*==============================================*/

static struct
{/*{{{*/
    char program[FILE_NM_SIZE];
    char dir[LONG_FILE_NM_SIZE+1];
    unsigned int is_date_switch;
    unsigned int files;
    unsigned int lines;
}/*}}}*/
_filelog={ ZEROS, ZEROS, IS_FALSE, 0, 0 };

int filelog_create(char *program, char *dir)
{/*{{{*/
    if(dir)
    {
        if(program)
        {
            char prog_dir[LONG_FILE_NM_SIZE+1];
            extract_dirname(program, prog_dir);
            extract_basename(program, _filelog.program);
            if(dir[0] == '/')
                strcpy(_filelog.dir, dir);
            else if(BUF_IS_CLR(dir))
                strcpy(_filelog.dir, prog_dir);
            else
                sprintf(_filelog.dir, "%s/%s", prog_dir, dir);
            if(create_dir(_filelog.dir))
                return(-2);
        }
        else
            return(-1);
    }
    else
    {   strcpy(_filelog.dir, "stdout");   }

    return(0);
}/*}}}*/

void filelog_set_date_switch()
{/*{{{*/
    _filelog.is_date_switch=IS_TRUE;
}/*}}}*/

int filelog_plain(char *msg)
{/*{{{*/
    char filelog_nm[LONG_FILE_NM_SIZE+1];
    FILE *fp;

    if(!msg)  return(RET_ERR_PARA);

    if(BUF_IS_CLR(_filelog.dir))
        return(0);
    if(strcmp(_filelog.dir, "stdout") == 0)
    {
        fputs(msg, stdout);
        fputs(RTNS, stdout);
        return(0);
    }

    sprintf(filelog_nm, "%s/%s.log",
            _filelog.dir, _filelog.program);
    if(_filelog.is_date_switch) // 日期切换
    {
        char now[TIMESTAMP_SIZE+1];
        sprintf(filelog_nm+strlen(filelog_nm), ".%.8s",
                curr_timestamp_str(now));
    }
    if(_filelog.files > 0)
    {
        sprintf(filelog_nm+strlen(filelog_nm), ".%d", _filelog.files);
    }
    if((fp=fopen(filelog_nm, "a")) == NULL)
        return(-1);
    fputs(msg, fp);
    fputs(RTNS, fp);
    fclose(fp);

    _filelog.lines++;
    if(_filelog.lines >= 1000000)
    {
        _filelog.files++;
        _filelog.lines=0;
    }

    return(0);
}/*}}}*/

int filelog(char *fmt, ...)
{/*{{{*/
    char now[TIMESTAMP_COMMON_SIZE+1];
    va_list arglist;
    char log_buf[BUFFER_SIZE];
    int offset;

    va_start(arglist, fmt);
    timestamp_fmt(curr_timestamp_str(now), NULL, now, NULL);
    offset=sprintf(log_buf, "%s -- ", now);
    vsnprintf(log_buf+offset, sizeof(log_buf)-offset-1, fmt, arglist);
    va_end(arglist);

    return(filelog_plain(log_buf));
}/*}}}*/
